﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO.Ports;
using System.IO;
using System.Globalization;
using System.Threading;
using System.Text.RegularExpressions;
using System.Diagnostics;

namespace Assembler
{
	class Program : Base
	{

        static void Main(string[] args)
        {
            string filein = "";
            string filetemp = "";
            //string test = "e";
            string type = "";
            string fileoutBin = "";
            string resfile = null;

            if (args.Length == 0)
            {
                //args = @"demo.s,system.s,uart.s,print.s,Sd2Card.s,ch375b.s,FileSystem.s,math.s,tjpeg.s,windows.s,dynamicMem.s demo.temp.s dos out.bin demo.res".Split(' ');
                args = @"demo.s,lib.s,glibc.s,soft-fp.s,quickjs.s demo.temp.s dos out.bin demo.res".Split(' ');
            }

            if (args.Length == 4)
            {
                filein = args[0];
                filetemp = args[1];
                type = args[2];
                fileoutBin = args[3];
            }
            else if (args.Length == 5)
            {
                filein = args[0];
                filetemp = args[1];
                type = args[2];
                fileoutBin = args[3];
                resfile = args[4];
            }
            else
            {
                throw new Exception("wrong args");
            }
            Console.WriteLine(filein);
            Console.WriteLine(filetemp);

            try
            {
                compile(filein, fileoutBin, filetemp, type, resfile);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
                //Console.ReadLine();
            }
            Console.WriteLine("done");
        }

		public static void compile(string filein, string fileoutBin, string filetemp, string type, string resfile)
        {
            Console.WriteLine("开始");
            Stopwatch tm = new Stopwatch();
            tm.Start();
            Console.WriteLine(resfile);
            List<Config> cfgs = Config.loadConfig(@"assembler\config.txt");
            List<RawLine> linesraw = new List<RawLine>();
            foreach (string f in filein.Split(','))
            {
                FileStream fs = new FileStream(f, FileMode.Open, FileAccess.Read);
                StreamReader sr = new StreamReader(fs);
                var all = sr.ReadToEnd();
                int lineNo = 0;
                string filename = null;
                foreach (var item in all.Split('\n'))
                {
                    lineNo++;
                    RawLine rl = new RawLine();
                    rl.lineNo = lineNo;


                    string temp = item.Trim();
                    if (temp.StartsWith(".file"))
                    {
                        string s = temp;
                        int start = s.IndexOf("\"") + 1;
                        s = s.Substring(start);
                        int end = s.IndexOf("\"");
                        s = s.Substring(0, end);
                        filename = s;
                        continue;
                    }
                    else if (temp.StartsWith(".string"))
                    {
                        temp = temp.Substring(".string ".Length);
                        temp = ".string " + temp;
                    }
                    else if (temp.StartsWith(".ascii"))
                    {
                        temp = temp.Substring(".ascii ".Length);
                        temp = ".ascii " + temp;
                    }
                    else
                    {
                        temp = temp.Replace('\t', ' ');
                        while (true)
                        {
                            if (temp.Contains("  "))
                            {
                                temp = temp.Replace("  ", " ");
                            }
                            else
                            {
                                break;
                            }
                        }
                    }
                    if (filename != null)
                    {
                        rl.filename = filename;
                    }
                    else
                    {
                        rl.filename = f;
                    }
                    rl.line = temp;
                    linesraw.Add(rl);
                }
                fs.Close();
            }

            int basePos;

            List<Line> lines = new List<Line>();

            lines.Add(Line.match(".section .text", null));
            if (type == "bios")
            {
                Console.WriteLine("type=bios");
                basePos = 0x40000000;
                lines.Add(Line.match("orhi r1, r0, 0x4200", null));//memory size at 4200:0000
                lines.Add(Line.match("ldw sp, 0(r1)", null));
                lines.Add(Line.match("jmpi main", null));
            }
            else if (type == "dos")
            {
                Console.WriteLine("type=dos");
                basePos = 0x00000000;
                lines.Add(Line.match("orhi r1, r0, 0x4200", null));//memory size at 4200:0000
                lines.Add(Line.match("ldw sp, 0(r1)", null));
                lines.Add(Line.match("jmpi main", null));
            }
            else if (type == "app")
            {
                Console.WriteLine("type=app");
                //sp由操作系统设置
                uint h = 0x80000000;
                basePos = (int)(h);
                lines.Add(Line.match("call main", null));
                lines.Add(Line.match("ret", null));
            }
            else if (type == "debug")
            {
                Console.WriteLine("type=debug");
                basePos = 0x00000000;
            }
            else
            {
                throw new Exception();
            }

            //结构化
            Console.WriteLine("结构化" + tm.ElapsedMilliseconds);
            foreach (var lineraw in linesraw)
            {
                string raw = lineraw.line;
                if (raw.Length == 1 && raw[0] == 0x1a)
                {
                    continue;
                }

                string temp = raw;
                if (temp.Length > 0)
                {
                    Line line = Line.match(temp, lineraw);
                    if (line == null)
                    {
                        throw new Exception("unknow line:" + raw);
                    }
                    lines.Add(line);
                }
            }

            lines.Add(Line.match(".section .text", null));
            lines.Add(Line.match("__getRes__:", null));
            lines.Add(Line.match("movhi r2, %hiadj(resource)", null));
            lines.Add(Line.match("addi r2, r2, %lo(resource)", null));
            lines.Add(Line.match("ret", null));
            lines.Add(Line.match("resource:", null));
            if (!String.IsNullOrEmpty(resfile))
            {
                FileStream fs = new FileStream(resfile, FileMode.Open, FileAccess.Read);
                byte[] buff = new byte[fs.Length];
                fs.Read(buff, 0, (int)fs.Length);
                Line l = new Line();
                l.dotType = Line.DotType.other;
                l.rawLine = new RawLine();
                l.res = buff;
                l.type = Line.LineType.res;
                //l.alignment = 1024;
                lines.Add(l);
                fs.Close();
            }

            lines.Add(Line.match(".section .text", null));
            lines.Add(Line.match("__eof__:", null));
            lines.Add(Line.match("movhi r2, %hiadj(eof)", null));
            lines.Add(Line.match("addi r2, r2, %lo(eof)", null));
            lines.Add(Line.match("ret", null));
            lines.Add(Line.match(".section .data", null));
            lines.Add(Line.match("eof:", null));
            lines.Add(Line.match(".zero 4", null));

            List<Line> linespass2 = new List<Line>();
            //展开宏
            Console.WriteLine("展开宏" + tm.ElapsedMilliseconds);



            var config_nop = new Config("nop");
            var config_br = new Config("br sym");
            var config_ble = new Config("ble reg, reg, sym");
            var config_bleu = new Config("bleu reg, reg, sym");
            var config_bgtu = new Config("bgtu reg, reg, sym");
            var config_bgt = new Config("bgt reg, reg, sym");
            var config_movi = new Config("movi reg, ins");
            var config_movui = new Config("movui reg, ins");
            var config_mov = new Config("mov reg, reg");
            var config_movhi = new Config("movhi reg, ins");
            var config_ldw = new Config("ldw reg, gprel");
            var config_stw = new Config("stw reg, gprel");
            var config_ldbu = new Config("ldbu reg, gprel");
            var config_stb = new Config("stb reg, gprel");
            var config_addi = new Config("addi reg, reg, gprel");
            var config_ldwio = new Config("ldwio reg, regins");
            var config_stwio = new Config("stwio reg, regins");
            var config_jmpi_sym = new Config("jmpi sym");
            var config_jmpi_ins = new Config("jmpi ins");


            foreach (var line in lines)
            {

                //bgtu rA, rB, label bltu rB, rA, label

                //cmpgt rC, rA, rB cmplt rC, rB, rA
                //cmpgti rB, rA, IMMED cmpgei rB, rA, (IMMED+1)
                //cmpgtu rC, rA, rB cmpltu rC, rB, rA
                //cmpgtui rB, rA, IMMED cmpgeui rB, rA, (IMMED+1)
                //cmple rC, rA, rB cmpge rC, rB, rA
                //cmplei rB, rA, IMMED cmplti rB, rA, (IMMED+1)
                //cmpleu rC, rA, rB cmpgeu rC, rB, rA
                //cmpleui rB, rA, IMMED cmpltui rB, rA, (IMMED+1)

                //movia rB, label orhi rB, r0, %hiadj(label)
                //addi, rB, r0, %lo(label)

                //subi rB, rA, IMMED addi rB, rA, (-IMMED)

                if (config_nop.match(line))//nop add r0, r0, r0
                {
                    linespass2.Add(Line.match("add r0, r0, r0", line.rawLine));
                }
                //br sym                       @          20 @                      0 @   6 @          000110      0x06
                else if (config_br.match(line))//ble rA, rB, label bge rB, rA, label
                {
                    linespass2.Add(Line.match("beq r0, r0, " + line.op1.text, line.rawLine));
                }
                else if (config_ble.match(line))//ble rA, rB, label bge rB, rA, label
                {
                    linespass2.Add(Line.match("bge " + line.op2.text + ", " + line.op1.text + ", " + line.op3.text, line.rawLine));
                }
                else if (config_bleu.match(line))//bleu rA, rB, label bgeu rB, rA, label
                {
                    linespass2.Add(Line.match("bgeu " + line.op2.text + ", " + line.op1.text + ", " + line.op3.text, line.rawLine));
                }
                else if (config_bgtu.match(line))//bgtu rA, rB, label bltu rB, rA, label
                {
                    linespass2.Add(Line.match("bltu " + line.op2.text + ", " + line.op1.text + ", " + line.op3.text, line.rawLine));
                }
                else if (config_bgt.match(line))//bgt rA, rB, label blt rB, rA, label
                {
                    linespass2.Add(Line.match("blt " + line.op2.text + ", " + line.op1.text + ", " + line.op3.text, line.rawLine));
                }
                else if (config_movi.match(line))//movi rB, IMMED addi, rB, r0, IMMED
                {
                    linespass2.Add(Line.match("addi " + line.op1.text + ", r0, " + line.op2.text, line.rawLine));
                }
                else if (config_movui.match(line))//movui rB, IMMED ori rB, r0, IMMED
                {
                    linespass2.Add(Line.match("ori " + line.op1.text + ", r0, " + line.op2.text, line.rawLine));
                }
                else if (config_mov.match(line))//mov rC, rA add rC, rA, r0
                {
                    linespass2.Add(Line.match("add " + line.op1.text + ", " + line.op2.text + ", r0", line.rawLine));
                }
                else if (config_movhi.match(line))//movhi rB, IMMED orhi rB, r0, IMMED
                {
                    linespass2.Add(Line.match("orhi " + line.op1.text + ", r0, " + line.op2.text, line.rawLine));
                }
                else if (config_ldw.match(line))//ldw r4, %gprel(c)(gp)
                {
                    //%gprel(immed32) Replace the immed32 address with an offset from the global pointer immed32 –_gp
                    //movhi	r1, %hiadj(c)
                    linespass2.Add(Line.match("orhi r1, r0, %hiadj(" + line.op2.sym + line.op2.getSymAdj() + ")", line.rawLine));
                    //addi	r1, r1, %lo(c)
                    //linespass2.Add(Line.match("addi r1, r1, %lo(" + line.op2.sym + line.op2.getSymAdj() + ")", line.filename));
                    //ldw	r4, 0(r1)
                    linespass2.Add(Line.match("ldw " + line.op1.text + ",r1, %lo(" + line.op2.sym + line.op2.getSymAdj() + ")", line.rawLine));
                }
                else if (config_stw.match(line))//stw r2, %gprel(Sd2Card_status_)(gp)
                {
                    //%gprel(immed32) Replace the immed32 address with an offset from the global pointer immed32 –_gp
                    //movhi	r1, %hiadj(c)
                    linespass2.Add(Line.match("orhi r1, r0, %hiadj(" + line.op2.sym + line.op2.getSymAdj() + ")", line.rawLine));
                    //addi	r1, r1, %lo(c)
                    //linespass2.Add(Line.match("addi r1, r1, %lo(" + line.op2.sym + line.op2.getSymAdj() + ")", line.filename));
                    //ldw	r4, 0(r1)
                    linespass2.Add(Line.match("stw " + line.op1.text + ", r1, %lo(" + line.op2.sym + line.op2.getSymAdj() + ")", line.rawLine));
                }
                else if (config_ldbu.match(line))//ldbu r4, %gprel(c)(gp)
                {
                    //%gprel(immed32) Replace the immed32 address with an offset from the global pointer immed32 –_gp
                    //movhi	r1, %hiadj(c)
                    linespass2.Add(Line.match("orhi r1, r0, %hiadj(" + line.op2.sym + line.op2.getSymAdj() + ")", line.rawLine));
                    //addi	r1, r1, %lo(c)
                    linespass2.Add(Line.match("addi r1, r1, %lo(" + line.op2.sym + line.op2.getSymAdj() + ")", line.rawLine));
                    //ldw	r4, 0(r1)
                    linespass2.Add(Line.match("ldbu " + line.op1.text + ", 0(r1)", line.rawLine));
                }
                //ldb
                //ldhu
                //ldh
                else if (config_stb.match(line))//stw r2, %gprel(Sd2Card_status_)(gp)
                {
                    //%gprel(immed32) Replace the immed32 address with an offset from the global pointer immed32 –_gp
                    //movhi	r1, %hiadj(c)
                    linespass2.Add(Line.match("orhi r1, r0, %hiadj(" + line.op2.sym + line.op2.getSymAdj() + ")", line.rawLine));
                    //addi	r1, r1, %lo(c)
                    linespass2.Add(Line.match("addi r1, r1, %lo(" + line.op2.sym + line.op2.getSymAdj() + ")", line.rawLine));
                    //ldw	r4, 0(r1)
                    linespass2.Add(Line.match("stb " + line.op1.text + ", 0(r1)", line.rawLine));
                }
                //sth
                else if (config_addi.match(line))//ldw r4, %gprel(c)(gp)
                {
                    if (line.op2.text != "gp")
                    {
                        throw new Exception("line.op2.sym != gp");
                    }
                    //%gprel(immed32) Replace the immed32 address with an offset from the global pointer immed32 –_gp
                    linespass2.Add(Line.match("orhi " + line.op1.text + ", r0, %hiadj(" + line.op3.sym + line.op3.getSymAdj() + ")", line.rawLine));
                    linespass2.Add(Line.match("addi " + line.op1.text + ", " + line.op1.text + ", %lo(" + line.op3.sym + line.op3.getSymAdj() + ")", line.rawLine));
                }
                else if (config_ldwio.match(line))//ldw r4, %gprel(c)(gp)
                {
                    //ldw	r4, 0(r1)
                    linespass2.Add(Line.match("ldw " + line.op1.text + ", " + line.op2.text, line.rawLine));
                }
                else if (config_stwio.match(line))//ldw r4, %gprel(c)(gp)
                {
                    //ldw	r4, 0(r1)
                    linespass2.Add(Line.match("stw " + line.op1.text + ", " + line.op2.text, line.rawLine));
                }
                else if (config_jmpi_sym.match(line))//jmpi
                {
                    //movhi	r1, %hiadj(c)
                    linespass2.Add(Line.match("orhi r1, r0, %hiadj(" + line.op1.sym + ")", line.rawLine));
                    //addi	r1, r1, %lo(c)
                    //linespass2.Add(Line.match("addi r1, r1, %lo(" + line.op1.sym + ")", line.filename));
                    //ldw	r4, 0(r1)
                    //linespass2.Add(Line.match("jmp r1", line.filename));
                    linespass2.Add(Line.match("jmp r1, %lo(" + line.op1.sym + ")", line.rawLine));
                }
                else if (config_jmpi_ins.match(line))//jmpi
                {
                    //movhi	r1, %hiadj(c)
                    linespass2.Add(Line.match("orhi r1, r0, %hiadj(" + line.op1.ins + ")", line.rawLine));
                    //addi	r1, r1, %lo(c)
                    //linespass2.Add(Line.match("addi r1, r1, %lo(" + line.op1.ins + ")", line.filename));
                    //ldw	r4, 0(r1)
                    //linespass2.Add(Line.match("jmp r1", line.filename));
                    linespass2.Add(Line.match("jmp r1, %lo(" + line.op1.ins + ")", line.rawLine));
                }
                else
                {
                    linespass2.Add(line);
                }
            }
            lines = linespass2;

            //区分符号属性
            Console.WriteLine("区分符号属性" + tm.ElapsedMilliseconds);
            {
                Line lastSym = null;
                foreach (var line in lines)
                {
                    if (line.type == Line.LineType.sym)
                    {
                        lastSym = line;
                    }
                    else if (line.type == Line.LineType.cmd)
                    {
                        if (lastSym != null)
                        {
                            lastSym.symType = Line.LineSymType.cmd;
                            lastSym = null;
                        }
                    }
                    else if (line.type == Line.LineType.sharp)
                    {
                    }
                    else
                    {
                        if (lastSym != null)
                        {
                            lastSym.symType = Line.LineSymType.data;
                            lastSym = null;
                        }
                    }
                }
            }

            int pos = basePos;
            List<Sym> syms = new List<Sym>();
            List<DataIns> dataInsList = new List<DataIns>();

            List<Alias> alias = new List<Alias>();

            //主处理，汇编
            Console.WriteLine("主处理，汇编" + tm.ElapsedMilliseconds);
            {
                foreach (var line in lines)
                {
                    if (line.type == Line.LineType.sym)
                    {
                        Sym sym = new Sym();
                        sym.name = line.sym;
                        sym.filename = line.rawLine.filename;
                        if (line.symType == Line.LineSymType.data)
                        {
                        }
                        else
                        {
                            sym.pos = pos;
                            syms.Add(sym);
                        }
                    }
                    else if (line.type == Line.LineType.cmd)
                    {
                        addCmd(cfgs, line, ref pos, dataInsList);
                    }
                    else
                    {
                    }

                    if (line.type == Line.LineType.dot && line.dotType == Line.DotType.set)
                    {
                        //.set	_ZN9ArrayListC1Ev(fake),_ZN9ArrayListC2Ev(real)
                        string[] temp = line.sym.Split(',');
                        Alias a = new Alias();
                        a.line = line;
                        a.real = temp[1];
                        a.fake = temp[0];
                        alias.Add(a);
                    }
                }
            }

            {
                Data dataTemp = null;
                foreach (var line in lines)
                {

                    if (line.type == Line.LineType.sym)
                    {
                        Sym sym = new Sym();
                        sym.name = line.sym;
                        sym.filename = line.rawLine.filename;
                        if (line.symType == Line.LineSymType.data)
                        {
                            flush(dataInsList, ref dataTemp, ref pos);
                            sym.pos = pos;
                            syms.Add(sym);

                        }
                        else
                        {
                        }
                    }
                    else if (line.type == Line.LineType.cmd)
                    {
                    }
                    else
                    {
                        addData(line, ref pos, syms, dataInsList, ref dataTemp);
                    }
                }

            }


            Dictionary<string, Sym> symMap = new Dictionary<string, Sym>();
            foreach (var sym in syms)
            {
                symMap.Add(sym.filename + "-" + sym.name, sym);
                if (!symMap.ContainsKey(sym.name))
                {
                    symMap.Add(sym.name, sym);
                }
            }

            while (true)
            {
                foreach (var a in alias)
                {
                    bool found = false;
                    if (symMap.ContainsKey(a.line.rawLine.filename + "-" + a.real))
                    {
                        symMap.Add(a.line.rawLine.filename + "-" + a.fake, symMap[a.line.rawLine.filename + "-" + a.real]);
                        found = true;
                    }
                    if (symMap.ContainsKey(a.real))
                    {
                        symMap.Add(a.fake, symMap[a.real]);
                        found = true;
                    }
                    if (found)
                    {
                        alias.Remove(a);
                        break;
                    }
                }
                if (alias.Count == 0)
                {
                    break;
                }
            }

            //链接符号
            Console.WriteLine("链接符号" + tm.ElapsedMilliseconds);
            foreach (var item in dataInsList)
            {
                if (item is Ins)
                {
                    var ins = item as Ins;
                    if (ins.op != null)
                    {
                        if (ins.op.sym != null)
                        {
                            Sym found = null;
                            //foreach (var sym in syms)
                            //{
                            //    if (ins.op.sym == sym.name && ins.op.filename == sym.filename)
                            //    {
                            //        found = sym; break;
                            //    }
                            //}
                            if (symMap.ContainsKey(ins.op.filename + "-" + ins.op.sym))
                            {
                                found = symMap[ins.op.filename + "-" + ins.op.sym];
                            }
                            if (found == null)
                            {
                                //foreach (var sym in syms)
                                //{
                                //    if (ins.op.sym == sym.name)
                                //    {
                                //        found = sym; break;
                                //    }
                                //}
                                if (symMap.ContainsKey(ins.op.sym))
                                {
                                    found = symMap[ins.op.sym];
                                }
                            }
                            if (found == null)
                            {
                                throw new Exception("not found:" + ins.op.sym + "," + ins.line.rawLine.filename + ":" + +ins.line.rawLine.lineNo + " " + ins.line.rawLine.line);
                            }
                            if (ins.format == 0)
                            {
                                if (ins.textformat == 15 || ins.textformat == 20)
                                {
                                    ins.IMM16 = found.pos - ins.pos - 4;
                                    if ((ins.IMM16 & 0xFFFF0000) != 0 && (ins.IMM16 & 0xFFFF0000) != 0xFFFF0000)
                                    {
                                        throw new Exception();
                                    }
                                }
                                else if (ins.textformat == 10)
                                {
                                    int temppos = found.pos;
                                    if (ins.op.symAdj != null)
                                    {
                                        temppos += ins.op.symAdj.Value;
                                    }
                                    ins.IMM16 = procAdj(ins.op, temppos);
                                    if ((ins.IMM16 & 0xFFFF0000) != 0 && (ins.IMM16 & 0xFFFF0000) != 0xFFFF0000)
                                    //if (ins.IMM16 >= 65535 || ins.IMM16 <= -32767)
                                    {
                                        throw new Exception();
                                    }
                                }
                                else
                                {
                                    throw new Exception();
                                }
                            }
                            else if (ins.format == 4)
                            {
                                int temppos = found.pos;
                                if (ins.op.symAdj != null)
                                {
                                    temppos += ins.op.symAdj.Value;
                                }
                                ins.IMM16 = procAdj(ins.op, temppos);
                                if ((ins.IMM16 & 0xFFFF0000) != 0 && (ins.IMM16 & 0xFFFF0000) != 0xFFFF0000)
                                //if (ins.IMM16 >= 65535 || ins.IMM16 <= -32767)
                                {
                                    throw new Exception();
                                }
                            }
                            else if (ins.format == 2)
                            {
                                //if (found.pos > 35000000)
                                //{
                                //	throw new Exception();
                                //}
                                ins.IMM26 = (found.pos >> 2) & 0xFFFFFF;//call 16M range
                            }
                        }
                        else
                        {
                            ins.IMM16 = procAdj(ins.op, ins.op.ins.Value);
                            if ((ins.IMM16 & 0xFFFF0000) != 0 && (ins.IMM16 & 0xFFFF0000) != 0xFFFF0000)
                            //if (ins.IMM16 > 65535 || ins.IMM16 < -32768)
                            {
                                throw new Exception();
                            }
                        }
                    }
                }

                if (item is Data)
                {
                    var data = item as Data;
                    if (data.sym != null)
                    {
                        Sym found = null;
                        int val = 0;
                        //foreach (var sym in syms)
                        //{
                        //    if (data.sym.name == sym.name && data.filename == sym.filename)
                        //    {
                        //        found = sym;
                        //        val = found.pos + data.sym.offset;
                        //        break;
                        //    }
                        //}
                        if (symMap.ContainsKey(data.filename + "-" + data.sym.name))
                        {
                            found = symMap[data.filename + "-" + data.sym.name];
                        }
                        if (found == null)
                        {
                            //foreach (var sym in syms)
                            //{
                            //    if (data.sym.name == sym.name)
                            //    {
                            //        found = sym;
                            //        val = found.pos + data.sym.offset;
                            //        break;
                            //    }
                            //}
                            if (symMap.ContainsKey(data.sym.name))
                            {
                                found = symMap[data.sym.name];
                            }
                        }
                        if (found != null)
                        {
                            val = found.pos + data.sym.offset;
                        }
                        if (found == null)
                        {
                            if (data.sym.name.Contains("class_type_infoE"))
                            {
                            }
                            else if (data.sym.name == "__cxa_pure_virtual")
                            {
                            }
                            else
                            {
                                throw new Exception("not found:" + data.sym + "," + data.line.rawLine.filename + ":" + +data.line.rawLine.lineNo + " " + data.line.rawLine.line);
                            }
                        }
                        byte[] src = new byte[4];
                        src[3] = (byte)((val >> 24) & 0xFF);
                        src[2] = (byte)((val >> 16) & 0xFF);
                        src[1] = (byte)((val >> 8) & 0xFF);//高8位
                        src[0] = (byte)(val & 0xFF);//低位
                        data.dataArray = src;
                        data.len = 4;
                    }
                }
            }

            Dictionary<int, string> symPosMap = new Dictionary<int, string>();

            //输出符号
            Console.WriteLine("输出符号" + tm.ElapsedMilliseconds);
            {
                FileStream fs2 = new FileStream(filetemp+".sym", FileMode.Create, FileAccess.Write);
                StreamWriter sw = new StreamWriter(fs2);
                foreach (var sym in syms)
                {
                    if (symPosMap.ContainsKey(sym.pos))
                    {
                        symPosMap[sym.pos] = symPosMap[sym.pos] + ";" + sym.name + "," + sym.filename;
                    }
                    else
                    {
                        symPosMap[sym.pos] = sym.name + "," + sym.filename;
                    }
                    sw.WriteLine(sym.pos + "," + sym.name);
                }
                sw.Flush();
                fs2.Close();
            }

            //写入二进制
            Console.WriteLine("写入二进制" + tm.ElapsedMilliseconds);
            {
                int len = pos;
                FileStream fs = new FileStream(fileoutBin, FileMode.Create, FileAccess.Write);
                //FileStream fs2 = new FileStream(fileout, FileMode.Create, FileAccess.Write);
                FileStream fs3 = new FileStream(filetemp, FileMode.Create, FileAccess.Write);

                BinaryWriter bw = new BinaryWriter(fs);
                //StreamWriter sw = new StreamWriter(fs2);
                StreamWriter sw3 = new StreamWriter(fs3);
                //bw.Write(len);
                //bw.Write(entry);

                int posx = 0;
                foreach (var item in dataInsList)
                {
                    string ssym = null;
                    if (symPosMap.ContainsKey(posx + basePos))
                    {
                        ssym = symPosMap[posx + basePos];
                    }
                    if (ssym == null)
                    {
                        ssym = "";
                    }
                    if (item is Ins)
                    {
                        var ins = item as Ins;
                        //[31]1bit:4 byte/8 byte
                        //[30:24]7bit:cmd
                        //[23:20]4bit:reg1
                        //[19:16]4bit:reg2
                        //[15:0]16bit:ins1
                        //[31:0]32bit:ins2
                        int insbin = 0;
                        if (ins.format == 0)//andi reg, reg, ins
                        {
                            insbin = (ins.bitregA << (31 - 5)) | (ins.bitregB << (31 - 10)) | ((ins.IMM16 & 0x0000FFFF) << (5)) | (ins.bitcmd);
                        }
                        else if (ins.format == 1)
                        {
                            //      1                     +5                           +5                            +5                      +6                    +5    +5
                            insbin = (ins.bitregA << (31 - 5)) | (ins.bitregB << (31 - 10)) | (ins.bitregC << (31 - 15)) | (ins.IMM6 << (5 + 5)) | (ins.bitcmd << (5)) | 0;//last 5bit is 00000=ext 不可用
                        }
                        else if (ins.format == 2)
                        {
                            insbin = (ins.IMM26 << 5) | (ins.bitcmd);
                        }
                        else if (ins.format == 3)
                        {
                            //insbin = (ins.bitregA << (31 - 5)) | (ins.bitregB << (31 - 10)) | (ins.bitregC << (31 - 15)) | (ins.IMM6 << (5 + 5)) | (ins.bitcmd << (5)) | 31; //11111
                            insbin = (ins.bitregA << (31 - 5)) | (ins.bitregB << (31 - 10)) | (ins.bitregC << (31 - 15)) | (ins.IMM6 << (5 + 5)) | (ins.bitcmd << (5)) | 3;// 00011
                        }
                        else if (ins.format == 4)
                        {
                            //insbin = (ins.bitregA << (31 - 5)) | (ins.bitregB << (31 - 10)) | (ins.bitregC << (31 - 15)) | (ins.IMM6 << (5 + 5)) | (ins.bitcmd << (5)) | 3;// 00011
                            insbin = (ins.bitregA << (31 - 5)) | ((ins.IMM16 & 0x0000FFFF) << (5 + 5)) | (ins.bitcmd << (5)) | 3;// 00011
                        }

                        int paritycheck = 0;
                        for (int i = 0; i < 31; i++)
                        {
                            paritycheck ^= (insbin >> i) & 1;
                        }

                        insbin |= (~paritycheck) << 31;

                        bw.Write(insbin);
                        //writehex(insbin, posx, sw);

                        sw3.WriteLine(getHex8(posx) + " [" + getHex8(insbin) + "] (" + ssym + ") " + ins.line.text);
                        //sw3.WriteLine(getHex8(posx));
                        //sw3.WriteLine(" [");
                        //sw3.WriteLine(getHex8(insbin));
                        //sw3.WriteLine("] (");
                        //sw3.WriteLine(ssym);
                        //sw3.WriteLine(") ");
                        //sw3.WriteLine(ins.line.text);
                        posx += 4;

                    }

                    if (item is Data)
                    {
                        var data = item as Data;
                        for (int i = 0; i < data.len; i += 4)
                        {
                            int val = 0;
                            if (i + 0 < data.dataArray.Length)
                            {
                                val |= (data.dataArray[i + 0]);
                            }
                            if (i + 1 < data.dataArray.Length)
                            {
                                val |= (data.dataArray[i + 1] << 8);
                            }
                            if (i + 2 < data.dataArray.Length)
                            {
                                val |= (data.dataArray[i + 2] << 16);
                            }
                            if (i + 3 < data.dataArray.Length)
                            {
                                val |= (data.dataArray[i + 3] << 24);
                            }
                            bw.Write(val);
                            //writehex(val, posx, sw);
                            sw3.WriteLine(getHex8(posx) + " [" + getHex8(val) + "] (" + ssym + ")");
                            //sw3.WriteLine(getHex8(posx));
                            //sw3.WriteLine(" [");
                            //sw3.WriteLine(getHex8(val));
                            //sw3.WriteLine("] (");
                            //sw3.WriteLine(ssym);
                            //sw3.WriteLine(")");
                            posx += 4;
                        }
                    }
                }

                //完成
                Console.WriteLine("完成" + tm.ElapsedMilliseconds);

                Console.WriteLine("size:" + posx);
                //sw.WriteLine(":00000001FF");

                //sw.Flush();
                //fs2.Close();

                sw3.Flush();
                fs3.Close();

                bw.Flush();
                fs.Close();

            }
            //Console.ReadLine();
        }

		public static void addCmd(List<Config> cfgs, Line line, ref int pos, List<DataIns> dataInsList)
		{
			Ins ins = new Ins();
			Config matchCfg = null;
			foreach (var cfg in cfgs)
			{
				if (cfg.match(line))
				{
					matchCfg = cfg;
					break;
				}
			}
			if (matchCfg == null)
			{
                throw new Exception("unknow cmd:" + line.text + "," + line.rawLine.filename);
			}
			else
			{
				try
				{
					switch (matchCfg.textformat)
					{
						case 10://andhi rB, rA, IMM16
							ins.bitregB = line.op1.reg.Value;
							ins.bitregA = line.op2.reg.Value;
							ins.op = line.op3;
							break;
						case 11://stw rB, byte_offset(rA)
							ins.bitregB = line.op1.reg.Value;
							ins.bitregA = line.op2.reg.Value;
							ins.IMM16 = line.op2.ins.Value;
							break;
                        case 12://stashi ins
                            ins.bitregB = 0;
                            ins.bitregA = 0;
                            ins.IMM16 = line.op1.ins.Value;
                            break;
                        case 15://bne rA, rB, label
							ins.bitregA = line.op1.reg.Value;
							ins.bitregB = line.op2.reg.Value;
							ins.op = line.op3;
							break;
						case 22://call sym
							ins.op = line.op1;
							break;
						case 30://add rC, rA, rB
							ins.bitregC = line.op1.reg.Value;
							ins.bitregA = line.op2.reg.Value;
							ins.bitregB = line.op3.reg.Value;
							break;
						case 31://slli reg, reg, ins
							ins.bitregC = line.op1.reg.Value;
							ins.bitregA = line.op2.reg.Value;
							ins.IMM6 = line.op3.ins.Value;
							break;
                        case 32://byteclip reg, reg, reg, imm6
							ins.bitregC = line.op1.reg.Value;
							ins.bitregA = line.op2.reg.Value;
							ins.bitregB = line.op3.reg.Value;
                            ins.IMM6 = line.op4.ins.Value;
                            break;

                        case 33://stash reg
							ins.bitregC = 0;
							ins.bitregA = line.op1.reg.Value;
							ins.bitregB = 0;
                            break;

                        case 34://custom ins, reg, reg, reg    @          33 @                      1 @  11111
                            ins.IMM6 = line.op1.ins.Value;
                            ins.bitregC = line.op2.reg.Value;
                            ins.bitregA = line.op3.reg.Value;
                            ins.bitregB = line.op4.reg.Value;
                            break;

						case 50://callr reg
							ins.bitregC = 31;
							ins.bitregA = line.op1.reg.Value;
							break;

						case 51://jmp reg
							ins.bitregA = line.op1.reg.Value;
							break;

						case 52://setirq reg, reg, reg, ins   @ addr, flg, result, isSetAddr
							ins.bitregA = line.op2.reg.Value;
							//ins.bitregB = line.op2.reg.Value;
                            ins.bitregB = line.op1.reg.Value;
							//ins.bitregC = line.op3.reg.Value;
							//ins.IMM6 = line.op4.ins.Value;
                            //ins.IMM6 = line.op2.ins.Value;
							break;

                        case 58://sti reg, reg                 @          58 @                      3 @  10111 @ return, data
                            ins.bitregC = line.op1.reg.Value;
                            ins.bitregB = line.op2.reg.Value;
                            //ins.IMM6 = line.op4.ins.Value;
                            break;

                        case 59://sti reg, reg                 @          58 @                      3 @  10111 @ return, data
							ins.bitregA = line.op1.reg.Value;
                            ins.op = line.op2;
                            break;

						case 53://stoff reg, ins
							ins.bitregA = line.op1.reg.Value;
                            ins.IMM6 = line.op2.ins.Value;
							break;

						case 54://hlt ins
							ins.IMM6 = line.op1.ins.Value;
							break;

						case 55://ret
							ins.bitregA = 31;
							break;

						case 56://reti
							ins.bitregA = 29;
							break;

						case 57://irqcall
							ins.bitregC = 31;
							break;

						default:
							throw new Exception("unknown text format:" + matchCfg.textformat);
					}
				}
				catch (Exception ex)
				{
					Console.WriteLine(ex.ToString());
					throw new Exception("error:" + line.text);
				}
				ins.line = line;
				ins.textformat = matchCfg.textformat;
				ins.format = matchCfg.insformat;
				ins.bitcmd = matchCfg.cmd;
				ins.pos = pos;
				dataInsList.Add(ins);
				pos += 4;
			}
		}

        static bool TryParse(string txt, out int val)
        {
            if (txt.StartsWith("0x"))
            {
                try
                {
                    val = Convert.ToInt32(txt.Substring(2), 16);
                    return true;
                }
                catch (Exception ex)
                {
                    val = 0;
                    return false;
                }
            }
            else
            {
                if (int.TryParse(txt, out val))
                {
                    return true;
                }
                else
                {
                    uint valu;
                    if (uint.TryParse(txt, out valu))
                    {
                        val = (int)valu;
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
            }
        }

        public static void addData(Line line, ref int pos, List<Sym> syms, List<DataIns> dataInsList, ref Data dataTemp)
        {
            if (line.type == Line.LineType.res)
            {
                flush(dataInsList, ref dataTemp, ref pos);
                Data data = new Data(line);
                data.dataArray = line.res;
                data.len = line.res.Length;
                pos += line.res.Length;
                dataInsList.Add(data);
                return;
            }

            //.comm	_gbuff, 64	 # 51
            //.comm	_tempstr, 16	 # 4
            if (line.text.StartsWith(".comm "))//.comm name, size, alignment
            {
                flush(dataInsList, ref dataTemp, ref pos);
                string txt = line.text.Trim().Substring(".comm ".Length);
                string[] arr = txt.Split(',');
                Sym dataSym = new Sym();
                dataSym.name = arr[0];
                dataSym.filename = line.rawLine.filename;
                dataSym.pos = pos;
                syms.Add(dataSym);

                int len = int.Parse(arr[1]);
                Data data = new Data(line);
                data.dataArray = new byte[len];
                data.len = len;
                pos += len;

                int diff = (data.len & 3);
                if (diff != 0)
                {
                    data.len += (4 - diff);
                    pos += (4 - diff);
                }
                dataInsList.Add(data);
                return;
            }

            if (line.text.StartsWith(".string \""))
            {
                string txt = line.text.Substring(".string \"".Length, line.text.Length - 1 - ".string \"".Length) + "\0";
                byte[] dataArray = unescape(txt);
                addData(ref dataTemp, dataArray, line, ref pos);
            }
            if (line.text.StartsWith(".ascii \""))
            {
                string txt = line.text.Substring(".ascii \"".Length, line.text.Length - 1 - ".ascii \"".Length);
                byte[] dataArray = unescape(txt);
                addData(ref dataTemp, dataArray, line, ref pos);
            }
            if (line.text.StartsWith(".4byte") || line.text.StartsWith(".long"))
            {
                flush(dataInsList, ref dataTemp, ref pos);
                Data data = new Data(line);
                string txt = line.text.Substring(".long ".Length);
                int val;
                if (TryParse(txt, out val))
                {
                    byte[] dataArray = new byte[4];
                    dataArray[3] = (byte)((val >> 24) & 0xFF);
                    dataArray[2] = (byte)((val >> 16) & 0xFF);
                    dataArray[1] = (byte)((val >> 8) & 0xFF);//高8位
                    dataArray[0] = (byte)(val & 0xFF);//低位
                    addData(ref dataTemp, dataArray, line, ref pos);
                }
                else
                {
                    SymOff symOff = new SymOff();
                    if (txt.Contains("+"))
                    {
                        String[] temp = txt.Split('+');
                        symOff.name = temp[0];
                        symOff.offset = Convert.ToInt32(temp[1]);
                    }
                    else
                    {
                        symOff.name = txt;
                        symOff.offset = 0;
                    }
                    data.sym = symOff;
                    data.filename = line.rawLine.filename;
                    data.len = 4;
                    pos += data.len;
                    dataInsList.Add(data);
                }
            }
            if (line.text.StartsWith(".zero "))
            {
                int s = int.Parse(line.text.Substring(".zero ".Length));
                byte[] dataArray = new byte[s];
                for (int i = 0; i < s; i++)
                {
                    dataArray[i] = 0;
                }
                addData(ref dataTemp, dataArray, line, ref pos);
            }

            if (line.text.StartsWith(".byte "))
            {
                string txt = line.text.Substring(".byte ".Length);
                int val;
                if (TryParse(txt, out val))
                {
                    byte[] dataArray = new byte[1];
                    dataArray[0] = (byte)(val & 0xFF);//低位
                    addData(ref dataTemp, dataArray, line, ref pos);
                }
                else
                {
                    throw new Exception("byte:" + txt);
                }
            }
            if (line.text.StartsWith(".short "))
            {
                string txt = line.text.Substring(".short ".Length);
                int val;
                if (TryParse(txt, out val))
                {
                    byte[] dataArray = new byte[2];
                    dataArray[1] = (byte)((val >> 8) & 0xFF);//高8位
                    dataArray[0] = (byte)(val & 0xFF);//低位
                    addData(ref dataTemp, dataArray, line, ref pos);
                }
                else
                {
                    throw new Exception("short:" + txt);
                }
            }

        }

        static void flush(List<DataIns> dataInsList, ref Data dataTemp, ref int pos)
        {
            if (dataTemp != null)
            {
                int diff = (dataTemp.len & 3);
                if (diff != 0)
                {
                    dataTemp.len += (4 - diff);
                    pos += (4 - diff);
                }
                dataInsList.Add(dataTemp);
                dataTemp = null;
            }
        }
        static void addData(ref Data dataTemp, byte[] dataArray, Line line, ref int pos)
        {
            if (dataTemp != null)
            {
                if (dataTemp.dataArray.Length < dataTemp.len + dataArray.Length)
                {
                    byte[] src = new byte[(dataTemp.len + dataArray.Length) * 2];
                    for (int i = 0; i < dataTemp.len; i++)
                    {
                        src[i] = dataTemp.dataArray[i];
                    }
                    dataTemp.dataArray = src;
                }
                for (int i = 0; i < dataArray.Length; i++)
                {
                    dataTemp.dataArray[dataTemp.len + i] = dataArray[i];
                }
                dataTemp.len += dataArray.Length;
                pos += dataArray.Length;
            }
            else
            {
                Data data = new Data(line);
                data.dataArray = dataArray;
                data.len = data.dataArray.Length;
                pos += data.len;
                dataTemp = data;
            }
        }
		//public static void writehex(int data, int posx, StreamWriter sw)
		//{
		//	//:04 0000 00 0302 0100 51
		//	int sum = 4;
		//	sw.Write(":04" + getHex4(posx, ref sum) + "00" + getHex8(data, ref sum));
		//	sw.Write(getHex2(0x100 - (sum & 0xFF)));
		//	sw.WriteLine();
		//}
        private static byte[] unescape(string s)
        {
            char[] arr = s.ToCharArray();
            List<byte> data = new List<byte>();
            int index = 0;
            try
            {
                while (true)
                {
                    char c = arr[index++];
                    if (c == '\\')
                    {
                        c = arr[index++];
                        if (c == '\'')// \'	用于表示字符常量'
                        {
                            data.Add((byte)'\'');
                        }
                        else if (c == '\"')// \"	用于表示一个字符串内部的双引号
                        {
                            data.Add((byte)'\"');
                        }
                        else if (c == '\\')// \\	用于表示一个反斜杠，防止它被解释为一个转义序列符
                        {
                            data.Add((byte)'\\');
                        }
                        else if (c == 'n')// \n	换行
                        {
                            data.Add((byte)'\n');
                        }
                        else if (c == 'r')// \r	回车
                        {
                            data.Add((byte)'\r');
                        }
                        else if (c == 't')// \t	水平制表符
                        {
                            data.Add((byte)'\t');
                        }
                        else if (c == 'x')// \xdd	dd表示2个十六进制数字 如\x30 0
                        {
                            int val = c - '0';
                            c = arr[index++];
                            val = val << 4;
                            val |= c - '0';
                            data.Add((byte)val);
                        }
                        else if (c >= '0' && c <= '7')// \ddd	ddd表示1~3个八进制的数字 如 \130X
                        {
                            int val = c - '0';
                            if (arr[index] >= '0' && arr[index] <= '7')
                            {
                                c = arr[index++];
                                val = val << 3;
                                int temp = c - '0';
                                val |= temp;
                                if (arr[index] >= '0' && arr[index] <= '7')
                                {
                                    c = arr[index++];
                                    val = val << 3;
                                    temp = c - '0';
                                    val |= temp;
                                }
                            }
                            data.Add((byte)val);
                        }
                        else
                        {
                            // \a	警告字符，蜂鸣
                            // \b	退格符
                            // \f	进制符
                            // \v	垂直制表符
                            // \?	在书写连续多个问号时使用，防止他们被解析为三字母词
                            data.Add((byte)' ');
                        }
                    }
                    else
                    {
                        data.Add((byte)c);
                    }
                }
            }
            catch
            {
            }

            return data.ToArray();
        }

	}
}
